local log = require "utilbelt.logger" ("DSSI")
local think = require "utilbelt.think"
local wait = require "utilbelt.wait"

local Type_SubmarineFile = LuaUserData.GetType("Barotrauma.SubmarineFile")

---@type { [string]:{ [string]: boolean } }
local registeredHookFunctions = {}

---@param name string
---@param identifier string
local function registerHookFunction(name, identifier)
    if registeredHookFunctions[name] == nil then registeredHookFunctions[name] = {} end
    registeredHookFunctions[name][identifier] = true
    log(("注册了一个挂钩函数，名称：'%s'，标识：'%s'。"):format(name, identifier))
end

---@param name string
---@param identifier string
---@param func function
function DSSI.Hook(name, identifier, func)
    Hook.Add(name, identifier, func)
    registerHookFunction(name, identifier)
end

---此处的参数`args['ingame']`被强制设为`false`
---@param args thinkargs
function DSSI.Think(args)
    args.ingame = false
    if think(args) then
        registerHookFunction("think", args.identifier)
    end
end

---@param name string
---@param identifier string
function DSSI.Unhook(name, identifier)
    if registeredHookFunctions[name] and registeredHookFunctions[name][identifier] then
        Hook.Remove(name, identifier)
        registeredHookFunctions[name][identifier] = nil
        log(("注销了一个挂钩函数，名称：'%s'，标识：'%s'。"):format(name, identifier))
    else
        log(("无法注销未被注册过的挂钩函数！名称：'%s'，标识：'%s'。"):format(name, identifier), 'w')
    end
end

---@param submarineInfo? userdata
local function inject(submarineInfo)
    if submarineInfo == nil then
        submarineInfo = Submarine.MainSub.Info
    end

    ---@type userdata
    local submarineContentPackage
    for contentPackage in ContentPackageManager.EnabledPackages.All do
        for submarineFile in contentPackage.GetFiles(Type_SubmarineFile) do
            if submarineFile.Path.Value == submarineInfo.FilePath then
                submarineContentPackage = contentPackage
                break
            end
        end
        if submarineContentPackage then break end
    end
    if submarineContentPackage == nil then
        log(("没有找到目标潜艇文件(路径：\"%s\")所属的内容包！"):format(submarineInfo.FilePath), 'w')
        return
    end
    local pathWithoutFileExtension = submarineInfo.FilePath:sub(1, -5)
    local file = pathWithoutFileExtension .. ".lua"
    if File.Exists(file) then
        log(("注入脚本文件(路径：\"%s\")"):format(file))
        loadfile(file)(submarineContentPackage.Path
            :gsub("\\", "/")
            :gsub(("/filelist.xml"):gsub("%a", function(c)
                return ("[%s%s]"):format(c:lower(), c:upper())
            end), ""), submarineInfo)
    else
        log(("没有找到脚本文件(路径：\"%s\")"):format(file), 'w')
    end
    Hook.Call("dssi.inject.after", submarineInfo)
end

if SERVER then
    Hook.Add("dssi.gameserver.startgame.preinit", function(selectedSub)
        inject(selectedSub)
    end)
else
    Hook.Add("dssi.gamesession.startround.preinit", function(submarineInfo)
        if submarineInfo then
            inject(submarineInfo)
        end
    end)
end

Hook.Add("roundEnd", "DSSI", function()
    log("开始注销挂钩函数……")
    for name, hookFunctions in pairs(registeredHookFunctions) do
        for identifier, _ in pairs(hookFunctions) do
            DSSI.Unhook(name, identifier)
        end
    end
end)

wait { inject, ingame = true }
